// 阿里云OSS服务
import OSS from 'ali-oss'
import { createHash } from 'crypto'
import path from 'path'

interface UploadResult {
  success: boolean
  url?: string
  key?: string
  error?: string
}

class AliyunOSSService {
  private client: OSS
  private bucketName: string

  constructor() {
    const region = process.env.ALIYUN_OSS_REGION || 'oss-cn-beijing'
    const accessKeyId = process.env.ALIYUN_OSS_ACCESS_KEY_ID || ''
    const accessKeySecret = process.env.ALIYUN_OSS_ACCESS_KEY_SECRET || ''
    this.bucketName = process.env.ALIYUN_OSS_BUCKET || ''

    if (!accessKeyId || !accessKeySecret || !this.bucketName) {
      throw new Error('阿里云OSS配置不完整，请检查环境变量：ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, ALIYUN_OSS_BUCKET')
    }

    this.client = new OSS({
      region,
      accessKeyId,
      accessKeySecret,
      bucket: this.bucketName
    })
  }

  /**
   * 生成文件的唯一键名
   * @param originalUrl 原始文件URL
   * @param prefix 前缀路径
   * @returns 唯一的文件键名
   */
  private generateFileKey(originalUrl: string, prefix: string = 'audio'): string {
    // 从URL中提取文件扩展名
    const urlPath = new URL(originalUrl).pathname
    const ext = path.extname(urlPath) || '.mp3'
    
    // 使用URL的MD5作为文件名，确保唯一性
    const hash = createHash('md5').update(originalUrl).digest('hex')
    const timestamp = Date.now()
    
    return `${prefix}/${timestamp}-${hash}${ext}`
  }

  /**
   * 从本地文件上传到OSS
   * @param localFilePath 本地文件路径
   * @param originalUrl 原始URL（用于生成键名）
   * @param prefix 存储路径前缀
   * @returns 上传结果
   */
  async uploadFromLocalFile(localFilePath: string, originalUrl: string, prefix: string = 'audio'): Promise<UploadResult> {
    try {
      console.log('开始上传本地文件到OSS:', localFilePath)

      // 检查文件是否存在
      const fs = await import('fs')
      if (!fs.existsSync(localFilePath)) {
        throw new Error(`本地文件不存在: ${localFilePath}`)
      }

      // 读取文件
      const fileBuffer = fs.readFileSync(localFilePath)
      console.log('读取本地文件完成，大小:', fileBuffer.length, 'bytes')

      // 生成OSS文件键名
      const fileKey = this.generateFileKey(originalUrl, prefix)

      console.log('开始上传到OSS:', fileKey)

      // 上传到OSS
      const result = await this.client.put(fileKey, fileBuffer, {
        headers: {
          'Content-Type': this.getContentTypeFromPath(localFilePath),
          'Cache-Control': 'public, max-age=31536000' // 缓存1年
        }
      })

      console.log('OSS上传成功:', result.url)

      // 使用永久URL（需要确保Bucket有公共读权限）
      const permanentUrl = result.url
      console.log('OSS永久URL:', permanentUrl)

      return {
        success: true,
        url: permanentUrl, // 使用永久URL
        key: fileKey
      }

    } catch (error) {
      console.error('OSS上传失败:', error)
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error'
      }
    }
  }

  /**
   * 从URL下载文件并上传到OSS（保留原有方法以兼容）
   * @param fileUrl 要上传的文件URL
   * @param prefix 存储路径前缀
   * @returns 上传结果
   */
  async uploadFromUrl(fileUrl: string, prefix: string = 'audio'): Promise<UploadResult> {
    try {
      console.log('开始从URL下载文件:', fileUrl)

      // 下载文件
      const response = await fetch(fileUrl)
      if (!response.ok) {
        throw new Error(`下载文件失败: HTTP ${response.status}`)
      }

      const buffer = await response.arrayBuffer()
      const fileBuffer = Buffer.from(buffer)

      console.log('文件下载完成，大小:', fileBuffer.length, 'bytes')

      // 生成OSS文件键名
      const fileKey = this.generateFileKey(fileUrl, prefix)

      console.log('开始上传到OSS:', fileKey)

      // 上传到OSS
      const result = await this.client.put(fileKey, fileBuffer, {
        headers: {
          'Content-Type': this.getContentType(fileUrl),
          'Cache-Control': 'public, max-age=31536000' // 缓存1年
        }
      })

      console.log('OSS上传成功:', result.url)

      // 使用永久URL（需要确保Bucket有公共读权限）
      const permanentUrl = result.url
      console.log('OSS永久URL:', permanentUrl)

      return {
        success: true,
        url: permanentUrl, // 使用永久URL
        key: fileKey
      }

    } catch (error) {
      console.error('OSS上传失败:', error)
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error'
      }
    }
  }

  /**
   * 批量上传本地文件
   * @param localFilePaths 本地文件路径列表
   * @param originalUrls 原始URL列表（用于生成键名）
   * @param prefix 存储路径前缀
   * @returns 上传结果列表
   */
  async batchUploadFromLocalFiles(localFilePaths: string[], originalUrls: string[], prefix: string = 'audio'): Promise<UploadResult[]> {
    const results: UploadResult[] = []

    for (let i = 0; i < localFilePaths.length; i++) {
      const localFilePath = localFilePaths[i]
      const originalUrl = originalUrls[i] || `local-file-${i}`

      const result = await this.uploadFromLocalFile(localFilePath, originalUrl, prefix)
      results.push(result)

      // 添加小延迟避免请求过于频繁
      await new Promise(resolve => setTimeout(resolve, 100))
    }

    return results
  }

  /**
   * 批量上传文件（保留原有方法以兼容）
   * @param fileUrls 文件URL列表
   * @param prefix 存储路径前缀
   * @returns 上传结果列表
   */
  async batchUploadFromUrls(fileUrls: string[], prefix: string = 'audio'): Promise<UploadResult[]> {
    const results: UploadResult[] = []

    for (const fileUrl of fileUrls) {
      const result = await this.uploadFromUrl(fileUrl, prefix)
      results.push(result)

      // 添加小延迟避免请求过于频繁
      await new Promise(resolve => setTimeout(resolve, 100))
    }

    return results
  }

  /**
   * 删除OSS文件
   * @param fileKey 文件键名
   * @returns 删除结果
   */
  async deleteFile(fileKey: string): Promise<boolean> {
    try {
      await this.client.delete(fileKey)
      console.log('OSS文件删除成功:', fileKey)
      return true
    } catch (error) {
      console.error('OSS文件删除失败:', error)
      return false
    }
  }

  /**
   * 批量删除文件
   * @param fileKeys 文件键名列表
   * @returns 删除结果
   */
  async batchDeleteFiles(fileKeys: string[]): Promise<boolean> {
    try {
      if (fileKeys.length === 0) return true
      
      await this.client.deleteMulti(fileKeys)
      console.log('OSS批量删除成功:', fileKeys.length, '个文件')
      return true
    } catch (error) {
      console.error('OSS批量删除失败:', error)
      return false
    }
  }

  /**
   * 检查文件是否存在
   * @param fileKey 文件键名
   * @returns 是否存在
   */
  async fileExists(fileKey: string): Promise<boolean> {
    try {
      await this.client.head(fileKey)
      return true
    } catch (error) {
      return false
    }
  }

  /**
   * 获取文件的Content-Type（从URL）
   * @param fileUrl 文件URL
   * @returns Content-Type
   */
  private getContentType(fileUrl: string): string {
    const ext = path.extname(new URL(fileUrl).pathname).toLowerCase()
    return this.getContentTypeFromExtension(ext)
  }

  /**
   * 获取文件的Content-Type（从本地路径）
   * @param filePath 文件路径
   * @returns Content-Type
   */
  private getContentTypeFromPath(filePath: string): string {
    const ext = path.extname(filePath).toLowerCase()
    return this.getContentTypeFromExtension(ext)
  }

  /**
   * 根据扩展名获取Content-Type
   * @param ext 文件扩展名
   * @returns Content-Type
   */
  private getContentTypeFromExtension(ext: string): string {
    const mimeTypes: { [key: string]: string } = {
      '.mp3': 'audio/mpeg',
      '.wav': 'audio/wav',
      '.m4a': 'audio/mp4',
      '.aac': 'audio/aac',
      '.ogg': 'audio/ogg',
      '.flac': 'audio/flac',
      '.wma': 'audio/x-ms-wma',
      '.amr': 'audio/amr',
      '.opus': 'audio/opus'
    }

    return mimeTypes[ext] || 'audio/mpeg'
  }

  /**
   * 获取文件的公网访问URL
   * @param fileKey 文件键名
   * @returns 公网URL
   */
  getPublicUrl(fileKey: string): string {
    return `https://${this.bucketName}.${this.client.options.region}.aliyuncs.com/${fileKey}`
  }

  /**
   * 生成签名URL（用于私有访问）
   * @param fileKey 文件键名
   * @param expires 过期时间（秒），默认1小时
   * @returns 签名URL
   */
  async getSignedUrl(fileKey: string, expires: number = 3600): Promise<string> {
    try {
      const url = await this.client.signatureUrl(fileKey, { expires })
      return url
    } catch (error) {
      console.error('生成签名URL失败:', error)
      throw error
    }
  }

  /**
   * 清理过期的临时文件
   * @param prefix 路径前缀
   * @param maxAge 最大年龄（毫秒），默认24小时
   * @returns 清理的文件数量
   */
  async cleanupExpiredFiles(prefix: string = 'audio', maxAge: number = 24 * 60 * 60 * 1000): Promise<number> {
    try {
      const cutoffTime = Date.now() - maxAge
      let deletedCount = 0
      
      // 列出文件
      const result = await this.client.list({ prefix, 'max-keys': 1000 })
      
      if (result.objects) {
        const expiredFiles = result.objects.filter(obj => {
          // 从文件名中提取时间戳
          const match = obj.name?.match(/\/(\d+)-/)
          if (match) {
            const timestamp = parseInt(match[1])
            return timestamp < cutoffTime
          }
          return false
        })
        
        if (expiredFiles.length > 0) {
          const fileKeys = expiredFiles.map(obj => obj.name!).filter(Boolean)
          await this.batchDeleteFiles(fileKeys)
          deletedCount = fileKeys.length
        }
      }
      
      console.log(`清理了 ${deletedCount} 个过期文件`)
      return deletedCount
      
    } catch (error) {
      console.error('清理过期文件失败:', error)
      return 0
    }
  }
}

export default AliyunOSSService
export type { UploadResult }
